WebRTC で P2P 接続するまでの流れ
WebRTC とは
通信技術の1つ
カメラの映像や音声などをやり取りできる
サーバーを仲介せず、直接PCとPCでリアルタイムな通信ができる(一般的にP2Pと呼ばれる方式)
WebRTC の実装
大部分はブラウザ側がやってくれる
やってくれないのは、自分の情報(IPアドレスなど)を相手に伝えること。
これを「シグナリング」と呼ぶ。
シグナリングの方法は何でも良い。
WebSocket でやりとりしても
ポーリングでやりとりしても
手紙を使ってやりとりしても良い。
シグナリング
シグナリングで相手に渡す情報は「SDP」「ICE Candidate」の2つが必要。
SDP:通信の内容が「音声か」「映像か」などの情報。お互いの通信方法を統一するために使用される
ICE Candidate:自分のIPアドレスや、使用するプロトコルなどの情報。お互いの通信経路を知るために使用される
上記の情報をやりとりする方法は自前で用意する必要がある。WebSocket でも手紙でもよい。
SDP の交換方法
基本的なやり方
自分が「オファー」を出し、相手がそれを受け取る
相手は「アンサー」を出し、自分はそれを受け取る。
以上で互いの SDP を知ることができる
自分と相手が同時にオファーを出したらどうするの?
それらの問題を解決するために Perfect Negotiation という交換方法がある
ICE Candidate の交換方法
基本的なやり方
こちらには「オファー」だの「アンサー」だのはない。
自分のIPアドレスやプロトコル(TCP / UDP)を含んだ ICE Candidate を相手に送信する。
相手も同様に送信する。
以上で互いの ICE Candidate を知ることができる
しかしここで問題になるのは「NAT」の存在である
NAT があることで自分のグローバルIPアドレスを知ることが困難になる
NAT が存在してても P2P 通信ができるようにするための技術を「NAT 越え」と呼んだりもする
NAT とは
1つのグローバルIPアドレスで、複数のデバイスがインターネットに接続するための技術
1つの Wi-Fi でスマホもPCもゲーム機も通信できてる人はだいたい NAT を使っている
P2P通信をする上では、非常に邪魔
ブラウザからグローバルIPアドレスを知ることができない
グローバルIPアドレスが分かったとしても、その先の複数あるデバイスの中で、特定のデバイスに繋がるかは分からない
使用されているNATの種類によって繋がるかどうかが変わる
NAT 越え(ICE)
NAT があったとしても P2P 接続を可能にするための技術
色々な方法があるが、WebRTC では「ICE」というやり方が採用されている
ICE は「STUN」「TURN」の2つのプロトコルを利用して NAT 越えをする
STUN
グローバルIPアドレスを知るためなどに活用される
基本的な流れ
ブラウザからSTUNサーバーにアクセスする
STUNサーバーはアクセスされたグローバルIPアドレスを返す
TURN
ポートが制限されていたり、NAT の種類的に通信ができない場合などに活用される
基本的な動きとしては、互いの通信をTURNサーバーが中継する
もはや P2P ではないが、高確率で接続をすることができる
ICE の基本的な流れ
ブラウザから IP アドレスなどを取得し、ICE Candidate の形式に記録する
STUN から IP アドレスなどを取得し、ICE Candidate の形式に記録する
TURN から IP アドレスなどを取得し、ICE Candidate の形式に記録する
これらの ICE Candidate をシグナリングによって相手に渡す。
受け取った ICE Candidate と自分の ICE Candidate を組み合わせてペアを作る
ペアの優先度を計算する(例:TURN はなるべく使いたくないので、優先度は低くなる)
これらペアの通信が上手くいくかを、全て試していく(UDPホールパンチ)
成功したものから優先度の高いものを採用する
P2P接続が完了
シグナリングに話を戻す
おさらい:「SDP」「ICE Candidate」の2つをやりとりできれば、後はブラウザがP2P接続してくれる
自分で STUN とかと通信する必要はない。ブラウザがやってくれる。
STUN と TURN の URI を渡してあげるだけで、ICE Candidate を生成してくれる
生成された ICE Candidate を WebSocket や手紙などを使って、相手に橋渡しするだけで済む
自分でやる必要があるのは「シグナリング」と「TURN サーバーの用意」の2つだけ!
(TRUN サーバーは STUN を兼ねるので、STUN サーバーは用意しなくてよい)
コードの話
ここまで WebRTC で P2P 通信を確立するまでの基本的な流れを示しました。
具体的なコードに関してはサンプル集を参考にするのがオススメです。
ここではサンプル集を読む上で、知っておいた方が良さそうなAPIを簡単に説明します。
RTCPeerConnection
ピアを表しているオブジェクト
このオブジェクトに TURN サーバーの URI を渡すと色々やってくれる
なので、このオブジェクトを操作したり、イベントを監視するのが基本となる
MediaDevices.getUserMedia()
カメラ映像や音声などの使用許可を求めるメソッド
カメラ映像や音声などのストリームを取得するメソッド
RTCDataChannel
テキストなどを通信するのに活用するオブジェクト
Ayame Labo について
上記で「自分でやる必要があるのは「シグナリング」と「TURN サーバーの用意」の2つだけ!」と書きましたが、この2つさえもやってくれるサービスです。
しかも無料で提供しているとのこと。時雨堂さん、すごすぎる。
まだ使ってみたことはないので、これから試していこうと思います。
感想
「WebRTC で P2P 接続する流れ」の概要は説明できたかなと思っています。詳細については W3C や RFC などを読んでいただければと思います。(自分も全然読みきれてないですが……)
実装する上で全体像を簡単に説明してくれるドキュメントがなかったので大変でした。
これを書いたことで、自分のような人が減ることを祈ります
P2P接続の確立に絞るように書きましたが、WebRTC には他にも様々な技術が使われています。これらを意識せず簡単に使える WebRTC の API すげぇなと思いました。
カメラやマイクを使うためのデバイス管理
音声や映像を圧縮して送信するためのコーデック
UDP上で信頼性のある通信を行うためのSCTP、などなど
関連リンク
WebRTC 殴り書きメモ
WebRTC の W3C
ICE の RFC の日本語訳
NAT の RFC の日本語訳
STUN の RFC の日本語訳
TRUN の RFC の日本語訳
「ビームと五目並べ」の制作